const isObject = val => val!==null && typeof val==='object'
// 转换value: 如果val是对象, 生成对应的proxy对象
const convert = val => isObject(val) ? reactive(val) : val

// 保存注册的effect函数
let activeEffect = null
/* 
用来注册副作用函数的工具函数
fn: 副作用函数
*/
export function effect(fn) {
  activeEffect = fn
  // 立即执行一次effect函数: 初始化显示, 调用属性的get, 触发track调用, 来收集依赖
  activeEffect()
  // 干掉注册的effect函数
  activeEffect = null
}

// 用来缓存effect函数的容器
const targetMap = new WeakMap()

/* 
收集依赖
*/
/* 
map{
  target对象1: map {
    属性名: set {
      effect1, effect2
    }
  }
}
*/
function track(target, key) {
  // 如果当前没有effect函数, 直接结束
  if(!activeEffect) return

  let depMap = targetMap.get(target) // map
  // 如果没有, 创建一个新的map, 并保存targetMap中
  if (!depMap) {
    depMap = new Map()
    targetMap.set(target, depMap)
  }

  let dep = depMap.get(key)  // set
  // 如果没有, 创建一个新的set, 并保存到depMap中
  if (!dep) {
    dep = new Set()
    depMap.set(key, dep)
  }

  // 将当前effect函数保存到dep中
  dep.add(activeEffect)
}

/* 
触发更新
*/
function trigger(target, key) {
  // 根据target 从targetMap中找到对应的map
  const depMap = targetMap.get(target)
  if (depMap) {
    // 根据key查找 depMap
    const dep = depMap.get(key)
    // 如果dep有值, 遍历其中的所有effect函数
    if (dep) {
      dep.forEach(effect => effect());
    }
  }
}



export function reactive(target) {

  // 如果target不是对象, 直接原样返回
  if (!isObject(target)) return target
  
  // 包含监视回调的处理器对象
  const handler = {
    get: function(target, key) {
      // console.log(`监视到读取${key}属性, 在此收集/追踪依赖`) // track
      track(target, key)
      const result = Reflect.get(target, key)
      return isObject(result) ? reactive(result) : result    // 懒代理
    },
    set: function(target, key, value) {
      // 如果值没有变, 直接返回
      const oldValue = Reflect.get(target, key)
      if (oldValue===value) return true

      const result = Reflect.set(target, key, value)
      if (result) {
        // console.log(`监视到修改或添加${key}属性, set在此派发/触发更新`) // trigger
        trigger(target, key)
      }
      return result
    },
    deleteProperty: function(target, key) {
     
      const result = Reflect.deleteProperty(target, key)
      if (result) {
        // console.log(`监视到删除${key}属性, delete在此派发/触发更新`)
        trigger(target, key)
      }
      return result
    }
  }

  return new Proxy(target, handler)
}


class RefImpl {
  constructor (value) {
    this._rawValue = value
    this._value = convert(value)
  }

  get value () {
    // console.log('监视到读value, 收集依赖')
    track(this, 'value')
    return this._value
  } 
  
  set value (newValue) {
    if (this._rawValue!==newValue) {
      this._rawValue = newValue
      this._value = convert(newValue)
      // console.log('监视到写value, 派发更新')
      trigger(this, 'value')
    }
  }
}

export function ref(value) {
  return new RefImpl(value)
}
